import http from 'http';
import fs from 'fs';
import path from 'path';
import { spawn } from 'child_process';
import { WebSocketServer, WebSocket } from 'ws';

const HTTP_PORT = 3000;
const WS_PORT = 3001;

const projectRoot = path.join(__dirname, '..');
const distRoot = projectRoot; // __dirname === dist/gateway
const serverJs = path.join(distRoot, 'server', 'server.js');
const webRoot = path.join(projectRoot, '..', 'web'); // compiled dist is inside dist/gateway, go up to project, then web

function log(...args: any[]) {
  console.log('[gateway]', ...args);
}

// Simple static file server for web/
const httpServer = http.createServer((req, res) => {
  const url = req.url || '/';
  let filePath: string;
  if (url === '/' || url === '/index.html') {
    filePath = path.join(webRoot, 'index.html');
  } else {
    filePath = path.join(webRoot, url.replace(/^\//, ''));
  }
  fs.readFile(filePath, (err, data) => {
    if (err) {
      res.writeHead(404, { 'Content-Type': 'text/plain' });
      res.end('Not found');
      return;
    }
    const ext = path.extname(filePath).toLowerCase();
    const contentType = ext === '.html' ? 'text/html' : ext === '.js' ? 'application/javascript' : 'text/plain';
    res.writeHead(200, { 'Content-Type': contentType });
    res.end(data);
  });
});
httpServer.listen(HTTP_PORT, () => {
  log(`HTTP server listening on http://localhost:${HTTP_PORT}/`);
});

// LSP stream parser for Content-Length framed messages
class LspStreamParser {
  private buffer: Buffer = Buffer.alloc(0);
  constructor(private onMessage: (jsonText: string) => void) {}

  feed(chunk: Buffer) {
    this.buffer = Buffer.concat([this.buffer, chunk]);
    while (true) {
      const headerEnd = this.buffer.indexOf('\r\n\r\n');
      if (headerEnd === -1) break;
      const header = this.buffer.slice(0, headerEnd).toString('utf8');
      const match = /Content-Length:\s*(\d+)/i.exec(header);
      if (!match) {
        // invalid header, drop
        this.buffer = this.buffer.slice(headerEnd + 4);
        continue;
      }
      const length = parseInt(match[1], 10);
      const totalNeeded = headerEnd + 4 + length;
      if (this.buffer.length < totalNeeded) break;
      const json = this.buffer.slice(headerEnd + 4, totalNeeded).toString('utf8');
      this.buffer = this.buffer.slice(totalNeeded);
      this.onMessage(json);
    }
  }
}

// Start WebSocket server and spawn LSP server per client
const wss = new WebSocketServer({ port: WS_PORT });
wss.on('listening', () => {
  log(`WS server listening on ws://localhost:${WS_PORT}/`);
});

wss.on('connection', (ws: WebSocket) => {
  log('WS client connected');
  const lspProc = spawn(process.execPath, [serverJs, '--stdio'], { cwd: path.join(distRoot) });
  lspProc.on('exit', (code) => log('LSP server exited with code:', code));
  lspProc.stderr.on('data', (d) => log('[server stderr]', d.toString()));

  const parser = new LspStreamParser((json) => {
    try {
      ws.send(json);
    } catch (e) {
      log('WS send error:', e);
    }
  });

  lspProc.stdout.on('data', (chunk) => parser.feed(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));

  ws.on('message', (data: any) => {
    try {
      const text = typeof data === 'string' ? data : data.toString();
      const payload = Buffer.from(text, 'utf8');
      const header = Buffer.from(`Content-Length: ${payload.length}\r\n\r\n`, 'utf8');
      lspProc.stdin.write(header);
      lspProc.stdin.write(payload);
    } catch (e) {
      log('Bridge write error:', e);
    }
  });

  ws.on('close', () => {
    log('WS client disconnected');
    try { lspProc.kill(); } catch {}
  });

  ws.on('error', (err: Error) => log('WS error:', err));
});